package com.CST.service.impl.cb;

import com.CST.common.api.ResultObject;
import com.CST.dto.cb.CountInComeDTO;
import com.CST.dto.cb.DateDTO;
import com.CST.entity.cb.*;
import com.CST.mapper.cst.cb.AmortizationDataMapper;
import com.CST.mapper.cst.cb.BaseInComeMapper;
import com.CST.mapper.cst.cb.EtlAccvouchMapper;
import com.CST.mapper.dataplatform.BdDepartmentMapper;
import com.CST.mapper.dataplatform.BdOrganizationMapper;
import com.CST.service.cb.IAmortizationDataService;
import com.CST.util.ExcelTransfer;
import com.CST.vo.cb.*;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class AmortizationDataServiceImpl implements IAmortizationDataService {

    @Autowired
    private AmortizationDataMapper amortizationDataMapper;

    @Autowired
    private EtlAccvouchMapper etlAccvouchMapper;

    @Autowired
    private BdOrganizationMapper bdOrganizationMapper;

    @Autowired
    private BdDepartmentMapper bdDepartmentMapper;

    @Autowired
    private BaseInComeMapper baseInComeMapper;

    @Override
    public ResultObject listAmortizationDataByDate(DateDTO dateDTO) {
        List<BdDepartment> bdDepartmentList = bdDepartmentMapper.selectList(null);
        List<ReportAmortizationDataVO> reportAmortizationDataVOList = new ArrayList<>();
        // 拿到所有临床类父级科室
        List<BdOrganization> clinicalDeptGroupList = bdOrganizationMapper.listClinicalDeptGroup("1");
//        List<BdOrganization> specialList =bdOrganizationMapper.listSpecial(dateDTO.getYear().toString(),dateDTO.getMonth().toString()); //医技科室自身产生收入科室
        List<BdOrganization> specialList = bdOrganizationMapper.listSpecial(dateDTO.getStarYear(), dateDTO.getStarMonth(), dateDTO.getEndYear(), dateDTO.getEndMonth()); //医技科室自身产生收入科室
        List<String> filterDeptList = new ArrayList<>();
        List<String> clinicalDeptList = clinicalDeptGroupList.stream().map(t -> t.getDeptCode()).distinct().collect(Collectors.toList());
        specialList.forEach(item -> {
            if (!clinicalDeptList.contains(item.getDeptCode())) {
                filterDeptList.add(item.getDeptCode());
            }
        });


        Map<String, List<BdOrganization>> clinicalDeptGroupListMap = clinicalDeptGroupList.stream().collect(Collectors.groupingBy(BdOrganization::getPDeptCode));
        // 获取所有临床类最终分摊成本表
        List<AmortizationData> clinicalAmortizationData = amortizationDataMapper.listClinicalAmortizationDataByDate(dateDTO, null, true, null);

        // 收入数据
        List<BaseInCome> baseInComeList = baseInComeMapper.listBaseInComeByDate(dateDTO);

        BigDecimal reduceAmount = BigDecimal.ZERO;

        if (filterDeptList != null && CollectionUtils.isNotEmpty(filterDeptList)) {
            //filterDeptList 中的所有收入总和 算至 1501 科室
            reduceAmount = baseInComeList.stream().filter(t -> filterDeptList.contains(t.getBillDeptCode())).map(t -> t.getAmount()).reduce(BigDecimal::add).get();
        }


        for (Object key : clinicalDeptGroupListMap.keySet()) {
            List<BdOrganization> thisDeptList = clinicalDeptGroupListMap.get(key.toString());
            List<String> thisDeptCodeList = thisDeptList.stream().map(t -> t.getDeptCode()).collect(Collectors.toList());

            // 计算科室对应的收入数据
            List<BaseInCome> inComeList = baseInComeList.stream().filter(t -> thisDeptCodeList.contains(t.getBillDeptCode())).collect(Collectors.toList());
            BigDecimal inComeAmount = inComeList.stream().map(t -> t.getAmount()).reduce(BigDecimal.ZERO, BigDecimal::add);
            BdDepartment bdDepartment = bdDepartmentList.stream().filter(t -> key.equals(t.getDeptCode())).findFirst().orElse(null);

            ReportAmortizationDataVO reportAmortizationDataVO = new ReportAmortizationDataVO();
            if (key.equals("1501")) {
                inComeAmount = inComeAmount.add(reduceAmount);
            }
            reportAmortizationDataVO.setInComeAmount(inComeAmount);

            reportAmortizationDataVO.setDeptCode(key.toString());
            reportAmortizationDataVO.setDeptName(bdDepartment.getDeptName());

            // 根据父级科室获取相应的分摊成本数据
            List<AmortizationData> dataList = clinicalAmortizationData.stream().filter(t -> key.equals(t.getPDeptCode())).collect(Collectors.toList());
            sortData(dataList, reportAmortizationDataVO);
            reportAmortizationDataVO.setSurplus(inComeAmount.subtract(reportAmortizationDataVO.getTotalSum()));
            reportAmortizationDataVOList.add(reportAmortizationDataVO);
        }

        reportAmortizationDataVOList = reportAmortizationDataVOList.stream().sorted(Comparator.comparing(ReportAmortizationDataVO::getDeptCode)).collect(Collectors.toList());

        /**
         * 2022.04.26
         * 添加重新分摊至科室科室数据
         */
        // 重新分摊至科室
        List<AmortizationData> unAmortizationData = amortizationDataMapper.listClinicalAmortizationDataByDate(dateDTO, null, false, null);
        if (CollectionUtils.isNotEmpty(unAmortizationData)) {
            ReportAmortizationDataVO reportAmortizationDataVO = new ReportAmortizationDataVO();
            reportAmortizationDataVO.setDeptCode("BBBBBBBBBB");
            reportAmortizationDataVO.setDeptName("重新分摊至科室");

            List<String> unAmortizationDept = unAmortizationData.stream().map(t -> t.getDeptCode()).collect(Collectors.toList());
            List<BaseInCome> unInComeList = baseInComeList.stream().filter(t -> unAmortizationDept.contains(t.getBillDeptCode())).collect(Collectors.toList());
            BigDecimal unInComeAmount = unInComeList.stream().map(t -> t.getAmount()).reduce(BigDecimal.ZERO, BigDecimal::add);
            reportAmortizationDataVO.setInComeAmount(unInComeAmount);
            sortData(unAmortizationData, reportAmortizationDataVO);

            //收入-成本  ??
            reportAmortizationDataVO.setSurplus(unInComeAmount.subtract(reportAmortizationDataVO.getTotalSum()));
            reportAmortizationDataVOList.add(reportAmortizationDataVO);
        }
        //对应有误或没有分摊规则的科室收入
        // 查询所有对应有错误的科室，即PDeptCode为null
        List<AmortizationData> errorAmortizationData = amortizationDataMapper.listClinicalAmortizationDataByDate(dateDTO, null, null, true);
        if (CollectionUtils.isNotEmpty(errorAmortizationData)) {
            ReportAmortizationDataVO reportAmortizationDataVO = new ReportAmortizationDataVO();
            reportAmortizationDataVO.setDeptCode("unKnow");
            reportAmortizationDataVO.setDeptName("对应有误科室");

            List<String> errorAmortizationDept = errorAmortizationData.stream().map(t -> t.getDeptCode()).collect(Collectors.toList());
            List<BaseInCome> errorInComeList = baseInComeList.stream().filter(t -> errorAmortizationDept.contains(t.getBillDeptCode())).collect(Collectors.toList());
            BigDecimal errorInComeAmount = errorInComeList.stream().map(t -> t.getAmount()).reduce(BigDecimal.ZERO, BigDecimal::add);
            reportAmortizationDataVO.setInComeAmount(errorInComeAmount);
            sortData(errorAmortizationData, reportAmortizationDataVO);

            //收入-成本  ??
            reportAmortizationDataVO.setSurplus(errorInComeAmount.subtract(reportAmortizationDataVO.getTotalSum()));
            reportAmortizationDataVOList.add(reportAmortizationDataVO);
        }


        return ResultObject.successReturn(reportAmortizationDataVOList);
    }

    @Override
    public ResultObject listAmortizationDataByPDeptCode(DateDTO dateDTO, String pDeptCode) {
        // 获取子父部门对应
        List<BdOrganization> bdOrganizationList = bdOrganizationMapper.listParentDeptGroup("1");
        List<BdOrganization> thisDeptList = bdOrganizationList.stream().filter(t -> pDeptCode.equals(t.getPDeptCode())).collect(Collectors.toList());
        List<String> thisDeptCodeList = thisDeptList.stream().map(t -> t.getDeptCode()).collect(Collectors.toList());
        // 获取全部数据 过滤子部门数据表
        List<AmortizationData> amortizationDataList = amortizationDataMapper.listAmortizationDataByDate(dateDTO);
        amortizationDataList = amortizationDataList.stream().filter(t -> thisDeptCodeList.contains(t.getDeptCode())).collect(Collectors.toList());
        // 如果BBBBBBBBBB为父级科室，那分摊成本应该是获取所有非临床成本
        if ("BBBBBBBBBB".equals(pDeptCode)) {
            amortizationDataList = amortizationDataMapper.listClinicalAmortizationDataByDate(dateDTO, null, false, null);
        }
        // 如果unKnow，那分摊成本为pDeptCode为null
        if ("unKnow".equals(pDeptCode)) {
            amortizationDataList = amortizationDataMapper.listClinicalAmortizationDataByDate(dateDTO, null, null, true);
        }
        Map<String, List<AmortizationData>> dataListMap = amortizationDataList.stream().collect(Collectors.groupingBy(AmortizationData::getDeptCode));

        List<ReportAmortizationDataVO> reportAmortizationDataVOList = new ArrayList<>();

        // 收入数据
        List<BaseInCome> baseInComeList = baseInComeMapper.listBaseInComeByDate(dateDTO);

        List<BdDepartment> bdDepartmentList = bdDepartmentMapper.selectList(null);
        for (Object key : dataListMap.keySet()) {
            List<AmortizationData> dataList = dataListMap.get(key);
            BdDepartment bdDepartment = bdDepartmentList.stream().filter(t -> key.equals(t.getDeptCode())).findFirst().orElse(null);
            List<BaseInCome> inComeList = baseInComeList.stream().filter(t -> key.equals(t.getBillDeptCode())).collect(Collectors.toList());
            BigDecimal inComeAmount = inComeList.stream().map(t -> t.getAmount()).reduce(BigDecimal.ZERO, BigDecimal::add);
            ReportAmortizationDataVO dataVO = new ReportAmortizationDataVO();
            dataVO.setInComeAmount(inComeAmount);
            dataVO.setDeptCode(key.toString());
            dataVO.setDeptName(bdDepartment.getDeptName());
            sortData(dataList, dataVO);
            dataVO.setSurplus(inComeAmount.subtract(dataVO.getTotalSum()));
            reportAmortizationDataVOList.add(dataVO);
        }
        return ResultObject.successReturn(reportAmortizationDataVOList);
    }

    @Override
    public List<Direct> noAmortizationDept(DateDTO dateDTO) {
        return amortizationDataMapper.noAmortizationDept(dateDTO);
    }

    @Override
    public ResultObject listCountInCome(CountInComeDTO countInComeDTO) {
        // 总计
        CountInComeDTO allDTO = new CountInComeDTO();
        BeanUtils.copyProperties(countInComeDTO, allDTO);
        allDTO.setName("总计");
        List<ReportCountInComeVO> allList = amortizationDataMapper.listAllCountInCome(allDTO);
        // 财政补贴 累计
        DateDTO dateDTO = new DateDTO();
        dateDTO.setStarYear(countInComeDTO.getStartYear());
        dateDTO.setEndYear(countInComeDTO.getEndYear());
        dateDTO.setStarMonth(countInComeDTO.getStartMonth());
        dateDTO.setEndMonth(countInComeDTO.getEndMonth());
        EtlAccvouch etlAccvouch = etlAccvouchMapper.listCaiZhenByDate(dateDTO);
        BigDecimal totalSubsidy = new BigDecimal(etlAccvouch.getMc());
        allList.get(0).setTotalSubsidy(totalSubsidy);
        //未纳入成本项 累计
        CostDataInfo noCostItemInfo = etlAccvouchMapper.listNoCostItemByDate(dateDTO);
        BigDecimal totalCount = new BigDecimal(noCostItemInfo.getAmount());
        allList.get(0).setTotalCount(totalCount);


        // 财政补贴 本期
        dateDTO.setStarYear(countInComeDTO.getEndYear());
        dateDTO.setEndYear(countInComeDTO.getEndYear());
        dateDTO.setStarMonth(countInComeDTO.getEndMonth());
        dateDTO.setEndMonth(countInComeDTO.getEndMonth());
        EtlAccvouch current = etlAccvouchMapper.listCaiZhenByDate(dateDTO);
        BigDecimal currentSubsidy = new BigDecimal(current.getMc());
        allList.get(0).setCurrentSubsidy(currentSubsidy);

        //未纳入成本项 本期
        CostDataInfo currentNoCostItemInfo = etlAccvouchMapper.listNoCostItemByDate(dateDTO);
        BigDecimal currentCount = new BigDecimal(currentNoCostItemInfo.getAmount());
        allList.get(0).setTotalCount(currentCount);


        // 总计-药品
        CountInComeDTO allDrug = new CountInComeDTO();
        BeanUtils.copyProperties(allDTO, allDrug);
        allDrug.setName("总计-药品");
        allDrug.setIsDrug("1");
        List<ReportCountInComeVO> allDrugList = amortizationDataMapper.listAllCountInCome(allDrug);
        // 总计-非药品
        CountInComeDTO allNoDrug = new CountInComeDTO();
        BeanUtils.copyProperties(allDTO, allNoDrug);
        allNoDrug.setName("总计-医疗");
        allNoDrug.setIsDrug("0");
        List<ReportCountInComeVO> allNoDrugList = amortizationDataMapper.listAllCountInCome(allNoDrug);


        // 门诊
        CountInComeDTO outDTO = new CountInComeDTO();
        BeanUtils.copyProperties(countInComeDTO, outDTO);
        outDTO.setType("2");
        outDTO.setClinicalPropertyId("1");
        outDTO.setName("门诊");
        List<ReportCountInComeVO> outList = amortizationDataMapper.listAllCountInCome1(outDTO);
        // 门诊-药品
        outDTO.setName("门诊-药品");
        outDTO.setIsDrug("1");
        List<ReportCountInComeVO> outDrugList = amortizationDataMapper.listAllCountInCome(outDTO);
        // 门诊-医疗
        outDTO.setName("门诊-医疗");
        outDTO.setIsDrug("0");
        List<ReportCountInComeVO> outNoDrugList = amortizationDataMapper.listAllCountInCome1(outDTO);

        // 住院
        CountInComeDTO inDTO = new CountInComeDTO();
        BeanUtils.copyProperties(countInComeDTO, inDTO);
        inDTO.setType("1");
        inDTO.setClinicalPropertyId("2");
        inDTO.setName("住院");
        List<ReportCountInComeVO> inList = amortizationDataMapper.listAllCountInCome(inDTO);
        // 门诊-药品
        inDTO.setName("住院-药品");
        inDTO.setIsDrug("1");
        List<ReportCountInComeVO> inDrugList = amortizationDataMapper.listAllCountInCome(inDTO);
        // 门诊-医疗
        inDTO.setName("住院-医疗");
        inDTO.setIsDrug("0");
        List<ReportCountInComeVO> inNoDrugList = amortizationDataMapper.listAllCountInCome(inDTO);


        List<ReportCountInComeVO> reportCountInComeVOList = new ArrayList<>();
        reportCountInComeVOList.addAll(allList);
        reportCountInComeVOList.addAll(allDrugList);
        reportCountInComeVOList.addAll(allNoDrugList);
        reportCountInComeVOList.addAll(outList);
        reportCountInComeVOList.addAll(outDrugList);
        reportCountInComeVOList.addAll(outNoDrugList);
        reportCountInComeVOList.addAll(inList);
        reportCountInComeVOList.addAll(inDrugList);
        reportCountInComeVOList.addAll(inNoDrugList);

        for (ReportCountInComeVO dataVO : reportCountInComeVOList) {
            // 收益
            dataVO.setCurrentSurplus(dataVO.getCurrentInCome().subtract(dataVO.getCurrentCost()));
            dataVO.setTotalSurplus(dataVO.getTotalInCome().subtract(dataVO.getTotalCost()));

            //收支结余(本期) =收益+财政补贴-未纳入成本项
            BigDecimal currentFinancial = dataVO.getCurrentSurplus().add(dataVO.getCurrentSubsidy())
                    .subtract(dataVO.getCurrentCount());
            dataVO.setCurrentFinancial(currentFinancial.setScale(2, BigDecimal.ROUND_HALF_UP));


            //收支结余(累计) =收益+财政补贴-未纳入成本项
            BigDecimal totalFinancial = dataVO.getTotalSurplus().add(dataVO.getTotalSubsidy())
                    .subtract(dataVO.getTotalCount());
            dataVO.setTotalFinancial(totalFinancial.setScale(2, BigDecimal.ROUND_HALF_UP));
        }
        return ResultObject.successReturn(reportCountInComeVOList);
    }

    /**
     * 医院各科室全程本表，按照会计分类汇总
     *
     * @param dateDTO
     * @return
     */
//    @Override
//    public List<ReportCostAccountVO> listAmortizationDataWithAccount(DateDTO dateDTO) {
//        // 直接成本数据
//        List<ReportCostAccountVO> amountList = amortizationDataMapper.listAmountWithAccountByDate(dateDTO, 0);
//        this.packageAmortizationData(dateDTO, null, amountList, "adminCost", true);
//        this.packageAmortizationData(dateDTO, null, amountList, "auxiliaryCost", true);
//        this.packageAmortizationData(dateDTO, null, amountList, "medicalTechnologyCost", true);
//        for (ReportCostAccountVO reportCostAccountVO : amountList) {
//            reportCostAccountVO.setTotalCost(reportCostAccountVO.getAdminCost()
//                    .add(reportCostAccountVO.getAuxiliaryCost())
//                    .add(reportCostAccountVO.getMedicalTechnologyCost()));
//        }
//        return amountList;
//    }
    @Override
    public List<ReportCostAccountVO> listAmortizationDataWithAccount(DateDTO dateDTO) {
        // 直接成本数据
        List<ReportCostAccountVO> amountList = amortizationDataMapper.listAmountWithAccountByDateNew(dateDTO);
        return amountList;
    }

    /**
     * 科室成本分析
     * 数据整理和归档方式类似于医院各剋是全成本表（会计分类汇总）
     * 区别在于：不需要根据AccountCode group by
     * 需要：时间范围+父级科室
     *
     * @param dateDTO   时间范围
     * @param pDeptCode 父级科室
     * @return
     */
    @Override
    public List<ReportCostAccountVO> deptCostAnalysis(DateDTO dateDTO, String pDeptCode) {
        // 获取直接成本
        List<ReportCostAccountVO> amountList = amortizationDataMapper.listAmortizationDataByDateAndPDeptWithDept(dateDTO, pDeptCode, null, 0, null);
        this.packageAmortizationData(dateDTO, pDeptCode, amountList, "adminCost", false);
        this.packageAmortizationData(dateDTO, pDeptCode, amountList, "auxiliaryCost", false);
        this.packageAmortizationData(dateDTO, pDeptCode, amountList, "medicalTechnologyCost", false);
        BigDecimal sum = amountList.stream().map(t -> (t.getAdminCost().add(t.getAuxiliaryCost()).add(t.getMedicalTechnologyCost()).add(t.getAmount()))).reduce(BigDecimal.ZERO, BigDecimal::add);
        // 占比记录
        BigDecimal one = BigDecimal.ONE;
        for (int i = 0; i < amountList.size(); i++) {
            ReportCostAccountVO reportCostAccountVO = amountList.get(i);
            // 计算全间接成本
            reportCostAccountVO.setTotalIndirectCost(reportCostAccountVO.getAdminCost()
                    .add(reportCostAccountVO.getAuxiliaryCost())
                    .add(reportCostAccountVO.getMedicalTechnologyCost()));
            // 计算总成本
            reportCostAccountVO.setTotalCost(reportCostAccountVO.getAmount().add(reportCostAccountVO.getTotalIndirectCost()));
            // 计算各分摊成本占比
            if (reportCostAccountVO.getTotalIndirectCost().compareTo(BigDecimal.ZERO) != 0) {
                reportCostAccountVO.setAdminProportion(reportCostAccountVO.getAdminCost().divide(reportCostAccountVO.getTotalIndirectCost(), 4).multiply(new BigDecimal(100)));
                reportCostAccountVO.setAuxiliaryProportion(reportCostAccountVO.getAuxiliaryCost().divide(reportCostAccountVO.getTotalIndirectCost(), 4).multiply(new BigDecimal(100)));
                reportCostAccountVO.setMedicalTechnologyProportion(reportCostAccountVO.getMedicalTechnologyCost().divide(reportCostAccountVO.getTotalIndirectCost(), 4).multiply(new BigDecimal(100)));
            }
            // 计算各科室总成本在父级科室总成本下的占比
            if (sum.compareTo(BigDecimal.ZERO) != 0 && reportCostAccountVO.getTotalCost().compareTo(BigDecimal.ZERO) != 0) {
                // 计算直接成本和间接成本占比
                reportCostAccountVO.setAmountProportion(reportCostAccountVO.getAmount().divide(reportCostAccountVO.getTotalCost(), 4).multiply(new BigDecimal(100)));
                reportCostAccountVO.setTotalIndirectProportion(reportCostAccountVO.getTotalIndirectCost().divide(reportCostAccountVO.getTotalCost(), 4).multiply(new BigDecimal(100)));
                if (i == amountList.size() - 1) {
                    reportCostAccountVO.setTotalProportion(one.multiply(new BigDecimal(100)));
                } else {
                    reportCostAccountVO.setTotalProportion(reportCostAccountVO.getTotalCost().divide(sum, 4).multiply(new BigDecimal(100)));
                    one = one.subtract(reportCostAccountVO.getTotalProportion().divide(new BigDecimal(100), 4));
                }
            }
        }
        return amountList;
    }

    /**
     * 传入时间+科室
     * 获取科室成本的分析的详情
     * 根据account进行分组和计算
     *
     * @return
     */
    @Override
    public List<ReportCostAccountVO> listDeptCostAnalysis(DateDTO dateDTO, String deptCode) {
        List<ReportCostAccountVO> amountList = amortizationDataMapper.listAmortizationDataByDateAndPDeptWithDept(dateDTO, null, deptCode, 0, true);
        amountList = this.packageAmortizationDataByAccount(amountList, dateDTO, "adminCost", deptCode);
        amountList = this.packageAmortizationDataByAccount(amountList, dateDTO, "auxiliaryCost", deptCode);
        amountList = this.packageAmortizationDataByAccount(amountList, dateDTO, "medicalTechnologyCost", deptCode);
        BigDecimal sum = amountList.stream().map(t -> (t.getAdminCost().add(t.getAuxiliaryCost()).add(t.getMedicalTechnologyCost()).add(t.getAmount()))).reduce(BigDecimal.ZERO, BigDecimal::add);
        // 占比记录
        BigDecimal one = BigDecimal.ONE;
        for (int i = 0; i < amountList.size(); i++) {
            ReportCostAccountVO reportCostAccountVO = amountList.get(i);
            // 计算全间接成本
            reportCostAccountVO.setTotalIndirectCost(reportCostAccountVO.getAdminCost()
                    .add(reportCostAccountVO.getAuxiliaryCost())
                    .add(reportCostAccountVO.getMedicalTechnologyCost()));
            // 计算总成本
            reportCostAccountVO.setTotalCost(reportCostAccountVO.getAmount().add(reportCostAccountVO.getTotalIndirectCost()));
            // 计算各分摊成本占比
            if (reportCostAccountVO.getTotalIndirectCost().compareTo(BigDecimal.ZERO) > 0) {
                reportCostAccountVO.setAdminProportion(reportCostAccountVO.getAdminCost().divide(reportCostAccountVO.getTotalIndirectCost(), 4).multiply(new BigDecimal(100)));
                reportCostAccountVO.setAuxiliaryProportion(reportCostAccountVO.getAuxiliaryCost().divide(reportCostAccountVO.getTotalIndirectCost(), 4).multiply(new BigDecimal(100)));
                reportCostAccountVO.setMedicalTechnologyProportion(reportCostAccountVO.getMedicalTechnologyCost().divide(reportCostAccountVO.getTotalIndirectCost(), 4).multiply(new BigDecimal(100)));
            }
            // 计算各科室总成本在父级科室总成本下的占比
            if (sum.compareTo(BigDecimal.ZERO) != 0 && reportCostAccountVO.getTotalCost().compareTo(BigDecimal.ZERO) != 0) {
                // 计算直接成本和间接成本占比
                reportCostAccountVO.setAmountProportion(reportCostAccountVO.getAmount().divide(reportCostAccountVO.getTotalCost(), 4).multiply(new BigDecimal(100)));
                reportCostAccountVO.setTotalIndirectProportion(reportCostAccountVO.getTotalIndirectCost().divide(reportCostAccountVO.getTotalCost(), 4).multiply(new BigDecimal(100)));
                if (i == amountList.size() - 1) {
                    reportCostAccountVO.setTotalProportion(one.multiply(new BigDecimal(100)));
                } else {
                    reportCostAccountVO.setTotalProportion(reportCostAccountVO.getTotalCost().divide(sum, 4).multiply(new BigDecimal(100)));
                    one = one.subtract(reportCostAccountVO.getTotalProportion().divide(new BigDecimal(100), 4));
                }
            }
        }
        return amountList;
    }

    @Autowired
    private ExcelTransfer<ReportAmortizationDataVO> excelTransfer;

    @Autowired
    private ExcelTransfer<ReportCountInComeVO> excelTransfer1;

    @Override
    public void exportExcel2(HttpServletResponse response, DateDTO dateDTO) throws IOException {
        List<ExcelVO> excel = new ArrayList<>();
        // head
        List<String> strings = new ArrayList<>();
        strings.add("直接成本");
        strings.add("间接成本");
        strings.add("全成本");
        List<String> info = new ArrayList<>();
        info.add("科室代码");
        info.add("科室名称");
        List<ExcelHead> excelHeads = new ArrayList<>();
        excelHeads.add(ExcelHead.builder().head("基本信息").cHead(info).build());
        excelHeads.add(ExcelHead.builder().head("人员经费").cHead(strings).build());
        excelHeads.add(ExcelHead.builder().head("药品费用").cHead(strings).build());
        excelHeads.add(ExcelHead.builder().head("卫生材料费用").cHead(strings).build());
        excelHeads.add(ExcelHead.builder().head("固定资产折旧费").cHead(strings).build());
        excelHeads.add(ExcelHead.builder().head("无形资产折旧费").cHead(strings).build());
        excelHeads.add(ExcelHead.builder().head("提取医疗风险基金").cHead(strings).build());
        excelHeads.add(ExcelHead.builder().head("其他费用").cHead(strings).build());
        excelHeads.add(ExcelHead.builder().head("合计").cHead(strings).build());

        List<List<String>> heads = getHead(excelHeads);
        // data
        List<List<Object>> otherData = new ArrayList<>();
        List<ReportAmortizationDataVO> dataInfo =
                (List<ReportAmortizationDataVO>) listAmortizationDataByDate(dateDTO).getData();
        dataInfo.forEach(e -> {
            List<Object> list = new ArrayList<>();
            list.add(e.getDeptCode());
            list.add(e.getDeptName());
            list.add(e.getDirectPersonnel());
            list.add(e.getIndirectPersonnel());
            list.add(e.getTotalPersonnel());
            list.add(e.getDirectDrug());
            list.add(e.getIndirectDrug());
            list.add(e.getTotalDrug());
            list.add(e.getDirectSanitaryMaterials());
            list.add(e.getIndirectSanitaryMaterials());
            list.add(e.getTotalSanitaryMaterials());
            list.add(e.getDirectFixed());
            list.add(e.getIndirectFixed());
            list.add(e.getTotalFixed());
            list.add(e.getDirectIntangible());
            list.add(e.getIndirectIntangible());
            list.add(e.getTotalIntangible());
            list.add(e.getDirectRisk());
            list.add(e.getIndirectRisk());
            list.add(e.getTotalRisk());
            list.add(e.getDirectOther());
            list.add(e.getIndirectOther());
            list.add(e.getTotalOther());
            list.add(e.getDirectSum());
            list.add(e.getIndirectSum());
            list.add(e.getTotalSum());
            otherData.add(list);
        });
        excel.add(ExcelVO.builder().sheet("sheet").listsHead(heads).listsData(otherData).build());
        excelTransfer.exportExcel(response, "临床服务类科室全成本表", excel);
    }

    @Override
    public void exportExcel3(HttpServletResponse response, DateDTO dateDTO) throws IOException {
        List<ExcelVO> excel = new ArrayList<>();
        // head
        List<String> strings = new ArrayList<>();
        strings.add("金额");
        strings.add("占比%");
        List<String> info = new ArrayList<>();
        info.add("科室代码");
        info.add("科室名称");
        List<ExcelHead> excelHeads = new ArrayList<>();
        excelHeads.add(ExcelHead.builder().head("基本信息").cHead(info).build());
        excelHeads.add(ExcelHead.builder().head("人员经费").cHead(strings).build());
        excelHeads.add(ExcelHead.builder().head("药品费用").cHead(strings).build());
        excelHeads.add(ExcelHead.builder().head("卫生材料费用").cHead(strings).build());
        excelHeads.add(ExcelHead.builder().head("固定资产折旧费").cHead(strings).build());
        excelHeads.add(ExcelHead.builder().head("无形资产折旧费").cHead(strings).build());
        excelHeads.add(ExcelHead.builder().head("提取医疗风险基金").cHead(strings).build());
        excelHeads.add(ExcelHead.builder().head("其他费用").cHead(strings).build());
        excelHeads.add(ExcelHead.builder().head("科室全成本合计").cHead(strings).build());
        List<String> cHead = new ArrayList<>();
        cHead.add("科室收入合计");
        excelHeads.add(ExcelHead.builder().head("科室收入合计").cHead(cHead).build());
        ArrayList<String> cHead1 = new ArrayList<>();
        cHead1.add("收入-成本");
        excelHeads.add(ExcelHead.builder().head("收入-成本").cHead(cHead1).build());
        List<List<String>> heads = getHead(excelHeads);

        // data
        List<List<Object>> otherData = new ArrayList<>();
        List<ReportAmortizationDataVO> dataInfo =
                (List<ReportAmortizationDataVO>) listAmortizationDataByDate(dateDTO).getData();
        dataInfo.forEach(e -> {
            List<Object> list = new ArrayList<>();
            list.add(e.getDeptCode());
            list.add(e.getDeptName());
            list.add(e.getTotalPersonnel());
            list.add(e.getProportionPersonnel());
            list.add(e.getTotalDrug());
            list.add(e.getProportionDrug());
            list.add(e.getTotalSanitaryMaterials());
            list.add(e.getProportionSanitaryMaterials());
            list.add(e.getTotalFixed());
            list.add(e.getProportionFixed());
            list.add(e.getTotalIntangible());
            list.add(e.getProportionIntangible());
            list.add(e.getTotalRisk());
            list.add(e.getProportionRisk());
            list.add(e.getTotalOther());
            list.add(e.getProportionOther());
            list.add(e.getTotalSum());
            list.add(e.getProportionSum());
            list.add(e.getInComeAmount());
            list.add(e.getSurplus());
            otherData.add(list);
        });
        excel.add(ExcelVO.builder().sheet("sheet").listsHead(heads).listsData(otherData).build());
        excelTransfer.exportExcel(response, "临床服务类科室全成本构成分析表", excel);
    }

    @Override
    public void exportExcel4(HttpServletResponse response, CountInComeDTO comeDTO) {
        excelTransfer1.exportExcel(response, (List<ReportCountInComeVO>) listCountInCome(comeDTO).getData(), "收益总表（财政补贴）",
                "sheet", ReportCountInComeVO.class);
    }

    @Override
    public void exportExcel6(HttpServletResponse response, DateDTO dateDTO) throws IOException {
        List<ExcelVO> excel = new ArrayList<>();
        // head
        List<String> headOld = new ArrayList<>();
        headOld.add("年");
        headOld.add("月");
        headOld.add("核算单元代码");
        headOld.add("核算单元名称");
//        headOld.add("核算定义代码");
//        headOld.add("核算定义名称");
        headOld.add("会计分类编码");
        headOld.add("会计分类名称");
        headOld.add("直接成本");
        headOld.add("行政分摊成本");
        headOld.add("医辅分摊成本");
        headOld.add("医技分摊成本");
        headOld.add("全成本");
        // data
        List<List<Object>> otherData = new ArrayList<>();
        List<ReportCostAccountVO> data = listAmortizationDataWithAccount(dateDTO);
        data.forEach(e -> {
            List<Object> list = new ArrayList<>();
            list.add(String.valueOf(e.getYear()));
            list.add(String.valueOf(e.getMonth()));
            list.add(e.getDeptCode());
            list.add(e.getDeptName());
//            list.add(e.getPDeptCode());
//            list.add(e.getPDeptName());
            list.add(e.getAccountCode());
            list.add(e.getAccountName());
            list.add(e.getAmount());
            list.add(e.getAdminCost());
            list.add(e.getAuxiliaryCost());
            list.add(e.getMedicalTechnologyCost());
            list.add(e.getTotalCost());
            otherData.add(list);
        });
        List<List<String>> otherHead = BaseCostServiceImpl.getHead(headOld);
        excel.add(ExcelVO.builder().sheet("各科室全成本表（会计分类汇总）").listsHead(otherHead).listsData(otherData).build());

        excelTransfer.exportExcel(response, "各科室全成本表（会计分类汇总）", excel);
    }


    @Autowired
    private ExcelTransfer<Direct> excelTransfer3;

    @Override
    public void exportExcel7(HttpServletResponse response, DateDTO dateDTO) {
        List<Direct> list = noAmortizationDept(dateDTO);
        excelTransfer3.exportExcel(response, list, "未分摊科室", "sheet", ExportDeptVO.class);
    }

    private List<List<String>> getHead(List<ExcelHead> headList) {
        List<List<String>> lists = new ArrayList<>();
        headList.forEach(e -> e.getCHead().forEach(c -> {
            List<String> list = new ArrayList<>();
            list.add(e.getHead());
            list.add(c);
            lists.add(list);
        }));
        return lists;
    }


    private ReportAmortizationDataVO sortData(List<AmortizationData> dataList, ReportAmortizationDataVO dataVO) {
        for (AmortizationData amortizationData : dataList) {
            String accountCode = amortizationData.getAccountCode();
            BigDecimal amount = amortizationData.getAmount();
            BigDecimal directCost = amortizationData.getDirectCost();
            BigDecimal indirectCost = amortizationData.getIndirectCost();
            switch (accountCode) {
                case "101":
//                    reportAmortizationDataVO.setPersonnel(reportAmortizationDataVO.getPersonnel().add(BigDecimal.valueOf(amount)));
                    dataVO.setDirectPersonnel(dataVO.getDirectPersonnel().add(amount.add(directCost)));
                    dataVO.setIndirectPersonnel(dataVO.getIndirectPersonnel().add(indirectCost));
                    dataVO.setTotalPersonnel((dataVO.getDirectPersonnel().add(dataVO.getIndirectPersonnel())));
                    break;
                case "102":
//                    reportAmortizationDataVO.setSanitaryMaterials(reportAmortizationDataVO.getSanitaryMaterials().add(BigDecimal.valueOf(amount)));
                    dataVO.setDirectSanitaryMaterials(dataVO.getDirectSanitaryMaterials().add(amount.add(directCost)));
                    dataVO.setIndirectSanitaryMaterials(dataVO.getIndirectSanitaryMaterials().add(indirectCost));
                    dataVO.setTotalSanitaryMaterials(dataVO.getDirectSanitaryMaterials().add(dataVO.getIndirectSanitaryMaterials()));
                    break;
                case "103":
//                    reportAmortizationDataVO.setDrug(reportAmortizationDataVO.getDrug().add(BigDecimal.valueOf(amount)));
                    dataVO.setDirectDrug(dataVO.getDirectDrug().add(amount.add(directCost)));
                    dataVO.setIndirectDrug(dataVO.getIndirectDrug().add(indirectCost));
                    dataVO.setTotalDrug(dataVO.getDirectDrug().add(dataVO.getIndirectDrug()));
                    break;
                case "104":
//                    reportAmortizationDataVO.setFixed(reportAmortizationDataVO.getFixed().add(BigDecimal.valueOf(amount)));
                    dataVO.setDirectFixed(dataVO.getDirectFixed().add(amount.add(directCost)));
                    dataVO.setIndirectFixed(dataVO.getIndirectFixed().add(indirectCost));
                    dataVO.setTotalFixed(dataVO.getDirectFixed().add(dataVO.getIndirectFixed()));
                    break;
                case "105":
//                    reportAmortizationDataVO.setIntangible(reportAmortizationDataVO.getIntangible().add(BigDecimal.valueOf(amount)));
                    dataVO.setDirectIntangible(dataVO.getDirectIntangible().add(amount.add(directCost)));
                    dataVO.setIndirectIntangible(dataVO.getIndirectIntangible().add(indirectCost));
                    dataVO.setTotalIntangible(dataVO.getDirectIntangible().add(dataVO.getIndirectIntangible()));
                    break;
                case "106":
//                    reportAmortizationDataVO.setRisk(reportAmortizationDataVO.getRisk().add(BigDecimal.valueOf(amount)));
                    dataVO.setDirectRisk(dataVO.getDirectRisk().add(amount.add(directCost)));
                    dataVO.setIndirectRisk(dataVO.getIndirectRisk().add(indirectCost));
                    dataVO.setTotalRisk(dataVO.getDirectRisk().add(dataVO.getIndirectRisk()));
                    break;
                case "107":
//                    reportAmortizationDataVO.setOther(reportAmortizationDataVO.getOther().add(BigDecimal.valueOf(amount)));
                    dataVO.setDirectOther(dataVO.getDirectOther().add(amount.add(directCost)));
                    dataVO.setIndirectOther(dataVO.getIndirectOther().add(indirectCost));
                    dataVO.setTotalOther(dataVO.getDirectOther().add(dataVO.getIndirectOther()));
                    break;
            }
//            reportAmortizationDataVO.setSum(reportAmortizationDataVO.getSum().add(BigDecimal.valueOf(amount)));
            dataVO.setDirectSum(dataVO.getDirectSum().add(amount.add(directCost)));
            dataVO.setIndirectSum(dataVO.getIndirectSum().add(indirectCost));
            dataVO.setTotalSum(dataVO.getDirectSum().add(dataVO.getIndirectSum()));
        }
        BigDecimal inAll = dataVO.getTotalSum();
        if (inAll.compareTo(BigDecimal.valueOf(0)) > 0) {
            dataVO.setProportionSum(BigDecimal.valueOf(100));
            dataVO.setProportionPersonnel(dataVO.getTotalPersonnel().divide(inAll, 5, BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(100)));
            dataVO.setProportionDrug(dataVO.getTotalDrug().divide(inAll, 5, BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(100)));
            dataVO.setProportionFixed(dataVO.getTotalFixed().divide(inAll, 5, BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(100)));
            dataVO.setProportionIntangible(dataVO.getTotalIntangible().divide(inAll, 5, BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(100)));
            dataVO.setProportionSanitaryMaterials(dataVO.getTotalSanitaryMaterials().divide(inAll, 5, BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(100)));
            dataVO.setProportionRisk(dataVO.getTotalRisk().divide(inAll, 5, BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(100)));
            dataVO.setProportionOther(dataVO.getTotalOther().divide(inAll, 5, BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(100)));
        }
        return dataVO;
    }

    /**
     * @param dateDTO
     * @param amountList 计算分摊成本的成本集合
     * @param type       分摊类型：医辅分摊，医技分摊，行政分摊
     * @param isAccount  是否按照会计分类汇总
     * @return
     */
    private List<ReportCostAccountVO> packageAmortizationData(DateDTO dateDTO, String pDeptCode, List<ReportCostAccountVO> amountList, String type, Boolean isAccount) {
        List<ReportCostAccountVO> reportCostAccountVOList = new ArrayList<>();
        if (isAccount == true) {
            switch (type) {
                case "adminCost":
                    reportCostAccountVOList = amortizationDataMapper.listAmountWithAccountByDate(dateDTO, 1);
                    break;
                case "auxiliaryCost":
                    reportCostAccountVOList = amortizationDataMapper.listAmountWithAccountByDate(dateDTO, 2);
                    break;
                case "medicalTechnologyCost":
                    reportCostAccountVOList = amortizationDataMapper.listAmountWithAccountByDate(dateDTO, 3);
                    break;
            }
        } else {
            switch (type) {
                case "adminCost":
                    reportCostAccountVOList = amortizationDataMapper.listAmortizationDataByDateAndPDeptWithDept(dateDTO, pDeptCode, null, 1, null);
                    break;
                case "auxiliaryCost":
                    reportCostAccountVOList = amortizationDataMapper.listAmortizationDataByDateAndPDeptWithDept(dateDTO, pDeptCode, null, 2, null);
                    break;
                case "medicalTechnologyCost":
                    reportCostAccountVOList = amortizationDataMapper.listAmortizationDataByDateAndPDeptWithDept(dateDTO, pDeptCode, null, 3, null);
                    break;
            }
        }
        for (ReportCostAccountVO reportCostAccountVO : amountList) {
            ReportCostAccountVO costAccountVO = new ReportCostAccountVO();
            if (isAccount == true) {
                costAccountVO = reportCostAccountVOList.stream().filter(
                        t -> reportCostAccountVO.getDeptCode().equals(t.getDeptCode())
                                && reportCostAccountVO.getPDeptCode().equals(t.getPDeptCode())
                                && reportCostAccountVO.getAccountCode().equals(t.getAccountCode())
                ).findFirst().orElse(null);
            } else {
                costAccountVO = reportCostAccountVOList.stream().filter(t ->
                                reportCostAccountVO.getPDeptCode().equals(t.getPDeptCode())
                                        && reportCostAccountVO.getDeptCode().equals(t.getDeptCode()))
                        .findFirst().orElse(null);
            }
            if (costAccountVO != null) {
                switch (type) {
                    case "adminCost":
                        reportCostAccountVO.setAdminCost(costAccountVO.getAdminCost());
                        break;
                    case "auxiliaryCost":
                        reportCostAccountVO.setAuxiliaryCost(costAccountVO.getAuxiliaryCost().subtract(reportCostAccountVO.getAdminCost()));
                        break;
                    case "medicalTechnologyCost":
                        reportCostAccountVO.setMedicalTechnologyCost(costAccountVO.getMedicalTechnologyCost().subtract(reportCostAccountVO.getAdminCost().add(reportCostAccountVO.getAuxiliaryCost())));
                        break;
                }
            }
        }
        return amountList;
    }

    /**
     * 根据会计分类整理成本分摊数据
     *
     * @param dateDTO
     * @param amountList 计算分摊成本的成本集合
     * @param type       分摊类型：医辅分摊，医技分摊，行政分摊
     */
    private List<ReportCostAccountVO> packageAmortizationDataByAccount(List<ReportCostAccountVO> amountList, DateDTO dateDTO, String type, String deptCode) {
        List<ReportCostAccountVO> reportCostAccountVOList = new ArrayList<>();
        switch (type) {
            case "adminCost":
                reportCostAccountVOList = amortizationDataMapper.listAmortizationDataByDateAndPDeptWithDept(dateDTO, null, deptCode, 1, true);
                break;
            case "auxiliaryCost":
                reportCostAccountVOList = amortizationDataMapper.listAmortizationDataByDateAndPDeptWithDept(dateDTO, null, deptCode, 2, true);
                break;
            case "medicalTechnologyCost":
                reportCostAccountVOList = amortizationDataMapper.listAmortizationDataByDateAndPDeptWithDept(dateDTO, null, deptCode, 3, true);
                break;
        }
        for (ReportCostAccountVO reportCostAccountVO : amountList) {
            ReportCostAccountVO costAccountVO = new ReportCostAccountVO();
            costAccountVO = reportCostAccountVOList.stream().filter(t -> reportCostAccountVO.getAccountCode() != null && reportCostAccountVO.getAccountCode().equals(t.getAccountCode())).findFirst().orElse(null);
            if (costAccountVO != null) {
                switch (type) {
                    case "adminCost":
                        reportCostAccountVO.setAdminCost(costAccountVO.getAdminCost());
                        break;
                    case "auxiliaryCost":
                        reportCostAccountVO.setAuxiliaryCost(costAccountVO.getAuxiliaryCost().subtract(reportCostAccountVO.getAdminCost()));
                        break;
                    case "medicalTechnologyCost":
                        reportCostAccountVO.setMedicalTechnologyCost(costAccountVO.getMedicalTechnologyCost().subtract(reportCostAccountVO.getAdminCost().add(reportCostAccountVO.getAuxiliaryCost())));
                        break;
                }
            }
        }
        return amountList;
    }
}
